#include "TreeModel.h"
#include "PointLabCore/IPointCloud.h"
#include <osg/Group>

#include <QIcon>

#include <cassert>
#include <QHeaderView>

TreeModel::TreeModel(QTreeView* view, QObject* parent) :
	QAbstractItemModel(parent)
{
	mdbTreeWidget = view;

	mdbTreeWidget->setModel(this);
	mdbTreeWidget->header()->hide();

	mdbTreeWidget->setDragEnabled(true);
	mdbTreeWidget->setAcceptDrops(true);
	mdbTreeWidget->setDropIndicatorShown(true);

	connect(mdbTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TreeModel::changeSelection);
}

TreeModel::~TreeModel()
{
}

void TreeModel::setRootNode(osg::Node *node)
{
	beginResetModel();
	mRootNode = node;
	endResetModel();
}

osg::Node* TreeModel::getRootNode()
{
	return mRootNode;
}

void TreeModel::addElement(osg::Node* node, bool autoExpand /*= true*/)
{
	if (!node)
	{
		return;
	}

	osg::Group* parentNode;
	if (node->getNumParents() > 0)
	{
		parentNode = node->getParent(0);
	}
	else
	{
		parentNode = mRootNode->asGroup();
		mRootNode->asGroup()->addChild(node);
	}

	QModelIndex insertNodeIndex = index(parentNode);
	int childPos = parentNode->getChildIndex(node);

	beginInsertRows(insertNodeIndex, childPos, childPos);

	endInsertRows();
}

void TreeModel::removeElement(osg::Node* node)
{
	if (!node)
	{
		return;
	}

	osg::Group* parentNode = node->getParent(0);

	if (!parentNode)
	{
		return;
	}

	int childPos = parentNode->getChildIndex(node);

	assert(childPos > 0);
	{
		beginRemoveRows(index(parentNode), childPos, childPos);

		parentNode->removeChild(node);

		endRemoveRows();
	}
}

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent /*= QModelIndex()*/) const
{
	if (!hasIndex(row, column, parent))
		return QModelIndex();

	osg::Node *node = (parent.isValid() ? static_cast<osg::Node*>(parent.internalPointer()) : mRootNode);

	if (!node)
		return QModelIndex();
	if (!node->asGroup())
	{
		return QModelIndex();
	}
	osg::Node *childNode = node->asGroup()->getChild(row);
	if (childNode)
		return createIndex(row, column, childNode);
	else
		return QModelIndex();
}

QModelIndex TreeModel::index(osg::Node* node)
{
	if (node == mRootNode)
	{
		return QModelIndex();
	}

	osg::Group* parentNode = node->getParent(0);

	if (!parentNode)
	{
		return QModelIndex();
	}

	int pos = parentNode->getChildIndex(node);

	return  createIndex(pos, 0, node);
}

QModelIndex TreeModel::parent(const QModelIndex &child) const
{
	if (!child.isValid())
		return QModelIndex();

	osg::Node *childNode = static_cast<osg::Node*>(child.internalPointer());

	if (!childNode)
		return QModelIndex();

	osg::Node*parentNode = childNode->getParent(0);
	if (!parentNode || parentNode == mRootNode)
		return QModelIndex();

	osg::Group* parentParentNode = parentNode->getParent(0);

	return createIndex(parentParentNode ? parentParentNode->getChildIndex(parentNode) : -1, 0, parentNode);
}

int TreeModel::rowCount(const QModelIndex &parent /*= QModelIndex()*/) const
{
	osg::Node* parentNode = 0;
	if (!parent.isValid())
		parentNode = mRootNode;
	else
		parentNode = static_cast<osg::Node*>(parent.internalPointer());

	if (parentNode&&parentNode->asGroup())
		return parentNode->asGroup()->getNumChildren();
	else
		return 0;
}

int TreeModel::columnCount(const QModelIndex &parent /*= QModelIndex()*/) const
{
	return 1;
}

QVariant TreeModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const
{
	if (!index.isValid())
		return QVariant();

	const osg::Node *node = static_cast<osg::Node*>(index.internalPointer());

	if (!node)
		return QVariant();

	if (role == Qt::DisplayRole)
	{
		QString baseName(node->getName().c_str());
		if (baseName.isEmpty())
			baseName = QStringLiteral("no name");
		//specific case

		return QVariant(baseName);
	}
	if (role == Qt::EditRole)
	{
		QString baseName(node->getName().c_str());

		return QVariant(baseName);
	}
	else if (role == Qt::DecorationRole)
	{
		return QIcon();
	}
	else if (role == Qt::CheckStateRole)
	{
		return (node->getNodeMask() == 0 ? Qt::Unchecked : Qt::Checked);
	}

	return QVariant();
}

bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role /*= Qt::EditRole*/)
{
	if (index.isValid())
	{
		if (role == Qt::EditRole)
		{
			if (value.toString().isEmpty())
				return false;

			osg::Node *item = static_cast<osg::Node*>(index.internalPointer());
			assert(item);
			if (item)
			{
				item->setName(value.toString().toStdString());

				emit dataChanged(index, index);
			}

			return true;
		}
		else if (role == Qt::CheckStateRole)
		{
			osg::Node *item = static_cast<osg::Node*>(index.internalPointer());
			assert(item);
			if (item)
			{
				if (value == Qt::Checked)
					item->setNodeMask(true);
				else
					item->setNodeMask(false);
			}

			return true;
		}
	}

	return false;
}

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
	if (!index.isValid())
		return 0;

	Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);

	//common flags
	defaultFlags |= (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);

	return defaultFlags;
}

void TreeModel::changeSelection(const QItemSelection & selected, const QItemSelection & deselected)
{
	//first unselect
	QModelIndexList deselectedItems = deselected.indexes();
	{
		for (int i = 0; i < deselectedItems.count(); ++i)
		{
			osg::Node* element = static_cast<osg::Node*>(deselectedItems.at(i).internalPointer());
			assert(element);
			if (element)
			{
			}
		}
	}

	//then select
	QModelIndexList selectedItems = selected.indexes();
	{
		for (int i = 0; i < selectedItems.count(); ++i)
		{
			osg::Node* element = static_cast<osg::Node*>(selectedItems.at(i).internalPointer());
			assert(element);
			if (element)
			{
			}
		}
	}

	emit selectionChanged();
}

PointLabCore::Uint TreeModel::getSelectedEntities(std::vector<osg::Node*>& selectedEntities)
{
	selectedEntities.clear();

	QItemSelectionModel* qism = mdbTreeWidget->selectionModel();
	QModelIndexList selectedIndexes = qism->selectedIndexes();

	try
	{
		int selCount = selectedIndexes.size();

		for (int i = 0; i < selCount; ++i)
		{
			osg::Node* node = static_cast<osg::Node*>(selectedIndexes[i].internalPointer());

			if (node)
			{
				selectedEntities.push_back(node);
			}
		}
	}
	catch (const std::bad_alloc&)
	{
		//not enough memory!
	}

	return selectedEntities.size();
}